1 module hip.api.net.utils;
2 import std.traits;
3 
4 
5 extern(C) @safe pure @nogc
6 {
7     ushort htons(ushort x);
8     uint htonl(uint x);
9     ushort ntohs(ushort x);
10     uint ntohl(uint x);
11 }
12 
13 T hton(T)(T data) @nogc nothrow pure
14 {
15 	version(WebAssembly)
16 	{
17 		return *(cast(T*)toNetworkBytes(data).ptr);
18 	}
19 	else
20 	{
21 		static if(T.sizeof == 2)
22 			return htons(data);
23 		else static if(T.sizeof == 4)
24 			return htonl(data);
25 	}
26 }
27 
28 size_t sizeofTypes(T...)() @nogc nothrow pure if(!hasDynamicArray!T)
29 {
30 	size_t ret;
31 	foreach (t; T)
32 		ret+= t.sizeof;
33 	return ret;
34 }
35 
36 bool hasDynamicArray(T)()
37 {
38 	static if(is(T == struct))
39 	{
40 		foreach(v; T.init.tupleof)
41 		{
42 			static if(hasDynamicArray!(typeof(v)))
43 				return true;
44 		}
45 		return false;
46 	}
47 	else
48 		return isDynamicArray!T;
49 }
50 
51 /**
52  * Used for determining if the data can be copied to stack and sent. This is the most efficient way to send data
53  * since it won't allocate on frame.
54  * Returns: If the type sequence has a dynamic array
55  */
56 bool hasDynamicArray(T...)()
57 {
58 	static foreach(t; T)
59 	{
60 		static if(hasDynamicArray!t)
61 			return true;
62 	}
63 	return false;
64 }
65 
66 @nogc nothrow pure
67 ubyte[sizeofTypes!T] toBytes(T...)(T input) if(!hasDynamicArray!(T))
68 {
69     typeof(return) ret = void;
70 	size_t offset;
71 	static foreach(i, t; T)
72 	{
73 		ret[offset..offset+t.sizeof] = toBytes!(t, t.sizeof)(input[i]);
74 		offset+= t.sizeof;
75 	}
76     return ret;
77 }
78 
79 void copyIntoMemory(T)(T struc, ubyte[] memory)
80 {
81 	static if(is(T == struct) && hasDynamicArray!T)
82 	{
83 		uint sz;
84 		foreach(v; struc.tupleof)
85 		{
86 			uint tSize = getSendTypeSize(v);
87 			copyIntoMemory(v, memory[sz..sz+tSize]);
88 			sz+= tSize;
89 		}
90 	}
91 	else static if(isDynamicArray!T)
92 	{
93 		memory[0..uint.sizeof] = toBytes(cast(uint)struc.length);
94 
95 		static if(hasDynamicArray!(typeof(T.init[0])))
96 		{
97 			size_t offset = uint.sizeof;
98 			foreach(i; 0..struc.length)
99 			{
100 				size_t sz = getSendTypeSize(struc[i]);
101 				copyIntoMemory(struc[i], memory[offset..offset+sz]);
102 				offset+= sz;
103 			}
104 		}
105 		else
106 		{
107 			size_t sz = struc.length * T.init[0].sizeof;
108 			memory[uint.sizeof..uint.sizeof+sz] = (cast(ubyte*)struc.ptr)[0..sz];
109 		}
110 	}
111 	else
112 	{
113 		memory[] = toBytes(struc);
114 	}
115 }
116 
117 ubyte[] toBytes(T...)(T input) if(hasDynamicArray!T)
118 {
119 	import std.stdio;
120 
121 	ubyte[] ret;
122 	size_t offset;
123 	foreach(i, t; T)
124 	{
125 		size_t sz = getSendTypeSize(input[i]);
126 		ret.length+= sz;
127 		copyIntoMemory(input[i], ret[offset..offset+sz]);
128 		offset+= sz;
129 	}
130 	return ret;
131 }
132 
133 uint getSendTypeSize(T...)(const T input) if(T.length > 1)
134 {
135 	uint sz;
136 	foreach(i, t; T)
137 	{
138 		sz+= getSendTypeSize(input[i]);
139 	}
140 	return sz;
141 }
142 
143 uint getSendTypeSize(T)(const T input = T.init) if(!is(T == struct) && !isDynamicArray!T)
144 {
145 	return T.sizeof;
146 }
147 uint getSendTypeSize(T)(const T input = T.init) if(isDynamicArray!T)
148 {
149 	uint sz = uint.sizeof;
150 	static if(hasDynamicArray!(T))
151 	{
152 		foreach(v; input)
153 		{
154 			sz+= getSendTypeSize(v);
155 		}
156 	}
157 	else
158 	{
159 		sz+= T.init[0].sizeof * input.length;
160 	}
161 	return sz;
162 }
163 
164 uint getSendTypeSize(T)(const T input = T.init) if(is(T == struct))
165 {
166 	uint sz;
167 	foreach(v; input.tupleof)
168 	{
169 		static if(hasDynamicArray!(typeof(v)))
170 		{
171 			sz+= getSendTypeSize(v);
172 		}
173 		else
174 		{
175 			sz+= typeof(v).sizeof;
176 		}
177 	}
178 	return sz;
179 }
180 
181 ubyte[N] toBytes(T, uint N = T.sizeof)(T input) @nogc nothrow pure
182 {
183     ubyte[N] ret = void;
184     ret[] = (cast(ubyte*)&input)[0..N];
185     return ret;
186 }
187 
188 bool isLittleEndian() @nogc nothrow pure
189 {
190     uint value = 1;
191     return (cast(ubyte*)&value)[0] == 1;
192 }
193 
194 ubyte[N] swapEndian(uint N)(ubyte[N] bytes) @nogc nothrow pure
195 {
196     ubyte[N] swapped = void;
197     foreach (i; 0 .. N)
198         swapped[i] = bytes[N - 1 - i];
199     return swapped;
200 }
201 
202 ubyte[] swapEndian(const ubyte[] bytes)
203 {
204     ubyte[] swapped;
205 	swapped.length = bytes.length;
206     foreach (i; 0 .. bytes.length)
207         swapped[i] = bytes[bytes.length - 1 - i];
208     return swapped;
209 }
210 
211 void swapEndianInPlace(ref ubyte[] bytes) @nogc nothrow pure
212 {
213     foreach (i; 0 .. bytes.length / 2)
214     {
215         auto tmp = bytes[i];
216         bytes[i] = bytes[bytes.length - 1 - i];
217         bytes[bytes.length - 1 - i] = tmp;
218     }
219 }
220 
221 ubyte[] toNetworkBytes(ubyte[] input)
222 {
223     return isLittleEndian ? swapEndian(input) : input;
224 }
225 
226 ubyte[N] toNetworkBytes(T, uint N = T.sizeof)(T input) @nogc nothrow pure
227 {
228     ubyte[N] ret = toBytes(input);
229     return isLittleEndian ? swapEndian(ret) : ret;
230 }
231 
232 ///Same implementation, must go back from big to little or kepe it as it is
233 alias fromNetworkBytes = toNetworkBytes;
234 
235 
236 void toNetworkBytesInPlace(ref ubyte[] bytes) @nogc nothrow pure
237 {
238 	if(isLittleEndian)
239 		swapEndianInPlace(bytes);
240 }
241 
242 alias fromNetworkBytesInPlace = toNetworkBytesInPlace;
243 
244 
245 
246 
247 ubyte[N] toNetwork(T, uint N = T.sizeof)(T data) @nogc nothrow pure
248 {
249 	ubyte[N] ret;
250 	static foreach(mem; __traits(allMembers, T))
251 	{{
252 		alias member = __traits(getMember, T, mem);
253 
254 		static if(member.sizeof == 1)
255 			ret[member.offsetof] = cast(ubyte)__traits(child, data, member);
256 		else static if(
257 			is(typeof(member) == uint) ||
258 			is(typeof(member) == ushort) ||
259 			is(typeof(member) == int) ||
260 			is(typeof(member) == short)
261 		)
262 			ret[member.offsetof..member.offsetof + member.sizeof] = toBytes(hton(__traits(child, data, member)));
263 		else
264 			ret[member.offsetof..member.offsetof + member.sizeof] = toNetworkBytes(__traits(child, data, member));
265 	}}
266 	return ret;
267 }
268 
269 
270 unittest
271 {
272 	struct Ultra
273 	{
274 		int[] a;
275 	}
276 
277 	struct EvenTester
278 	{
279 		Ultra[] tester;
280 	}
281 	assert(hasDynamicArray!Ultra);
282 	Ultra b;
283 	b.a~= [500, 200];
284 
285 	EvenTester t;
286 	t.tester~= b;
287 
288 	// assert(getSendTypeSize(b) == 12);
289 	// import hip.api.net.hipnet;
290 
291 	// assert(getSendTypeSize(NetHeader.init, b) == 17);
292 	// assert(toBytes(NetHeader.init, b).length == 17);
293 
294 	import std.stdio;
295 
296 	// writeln = getSendTypeSize(t.tester);
297 }
298 
299 unittest
300 {
301 	import hip.api.net.server;
302 	import hip.api.net.controller;
303 	import hip.api.net.hipnet;
304 
305 	ConnectedClientsResponse resp;
306 
307 	ConnectedClient c = ConnectedClient(NetConnectInfo(NetIPAddress(null, 0, IPType.ipv4), 0));
308 	resp.clients~= [c];
309 
310 	ubyte[] sendData = getNetworkFormattedData(resp, MarkedNetReservedTypes.get_connected_clients);
311 	ubyte[] respData = fromNetworkBytes(sendData);
312 
313 	assert(getSendTypeSize(c) == 11);
314 
315 
316 	respData = respData[NetHeader.sizeof..$-1];
317 	assert(respData.length == 15);
318 
319 	auto interpreted =  getNetworkStruct!(ConnectedClientsResponse)(respData);
320 
321 	// writeln = interpreted.type;
322 	assert(interpreted == resp);
323 }